package ca.scotthyndman.web.swf;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * <code>BuildInfo</code> objects describe the options that can be used when
 * building a SWF. A build info can represent one of two different compile
 * operations.
 * </p>
 * <ol>
 * <li>The first build operation is using MTASC alone to build the SWF. This is
 * accomplished by using the {@link BuildInfo#BuildInfo(String, int, int, int)},
 * {@link BuildInfo#BuildInfo(String, int, int, int, List)} or
 * {@link BuildInfo#BuildInfo(String, int, int, int, String)} constructors.</li>
 * <li>The second build operation uses swfmill and MTASC to build the SWF. This
 * is accomplished by using the {@link BuildInfo#BuildInfo(String, String)},
 * {@link BuildInfo#BuildInfo(String, String, String)} or
 * {@link BuildInfo#BuildInfo(String, String, List)} constructors.</li>
 * </ol>
 * <p>
 * Please note that in the case where a build is using swfmill, all import paths
 * must be relative to Java's working directory, not the XML file.
 * </p>
 * 
 * @author Scott Hyndman
 */
public class BuildInfo {

	private int width;

	private int height;

	private int fps;

	private String traceFunction = null;

	private int version = 8;

	private boolean strict = false;

	private boolean main = true;

	private String fileToCompile;

	private String library;

	private List<String> packedPackages;

	private List<String> exclusions;

	private boolean excludeAllButPacked = false;

	private List<String> classPaths;

	/**
	 * Creates a new build info instance. The working directory will be used as
	 * the class path.
	 * 
	 * @param fileToCompile
	 *            The path to the class file that is compiled first. All
	 *            dependencies will also be compiled. Please note that this path
	 *            should be relative to the root of the file's class path.
	 * @param width
	 *            The width of the resulting swf
	 * @param height
	 *            The height of the resulting swf
	 * @param fps
	 *            The frame rate of the resulting swf
	 */
	public BuildInfo(String fileToCompile, int width, int height, int fps) {
		this(fileToCompile, width, height, fps, (List<String>) null);
	}

	/**
	 * Creates a new build info instance.
	 * 
	 * @param fileToCompile
	 *            The path to the class file that is compiled first. All
	 *            dependencies will also be compiled. Please note that this path
	 *            should be relative to the root of the file's class path.
	 * @param width
	 *            The width of the resulting swf
	 * @param height
	 *            The height of the resulting swf
	 * @param fps
	 *            The frame rate of the resulting swf
	 * @param classPath
	 *            A directory path (absolute, or relative to working directory)
	 *            that will be searched for ActionScript classes
	 */
	public BuildInfo(String fileToCompile, int width, int height, int fps,
			String classPath) {
		this(fileToCompile, width, height, fps, listWithString(classPath));
	}

	/**
	 * Creates a new build info instance.
	 * 
	 * @param fileToCompile
	 *            The path to the class file that is compiled first. All
	 *            dependencies will also be compiled. Please note that this path
	 *            should be relative to the root of the file's class path.
	 * @param width
	 *            The width of the resulting swf
	 * @param height
	 *            The height of the resulting swf
	 * @param fps
	 *            The frame rate of the resulting swf
	 * @param classPaths
	 *            A list of directory paths (absolute, or relative to working
	 *            directory) that will be searched for ActionScript classes
	 */
	public BuildInfo(String fileToCompile, int width, int height, int fps,
			List<String> classPaths) {
		this.setFileToCompile(fileToCompile);
		this.setWidth(width);
		this.setHeight(height);
		this.setFPS(fps);
		this.setClassPaths(classPaths);
	}

	/**
	 * Creates a new build info instance. The working directory will be used as
	 * the class path.
	 * 
	 * @param fileToCompile
	 *            The path to the class file that is compiled first. All
	 *            dependencies will also be compiled. Please note that this path
	 *            should be relative to the root of the file's class path.
	 * @param library
	 *            The path to the swfmill library XML file that will be used to
	 *            compile assets.
	 */
	public BuildInfo(String fileToCompile, String library) {
		this(fileToCompile, library, (List<String>) null);
	}

	/**
	 * Creates a new build info instance.
	 * 
	 * @param fileToCompile
	 *            The path to the class file that is compiled first. All
	 *            dependencies will also be compiled. Please note that this path
	 *            should be relative to the root of the file's class path.
	 * @param library
	 *            The path to the swfmill library XML file that will be used to
	 *            compile assets.
	 * @param classPath
	 *            A directory path (absolute, or relative to working directory)
	 *            that will be searched for ActionScript classes
	 */
	public BuildInfo(String fileToCompile, String library, String classPath) {
		this(fileToCompile, library, listWithString(classPath));
	}

	/**
	 * Creates a new compiler info instance.
	 * 
	 * @param fileToCompile
	 *            The path to the class file that is compiled first. All
	 *            dependencies will also be compiled. Please note that this path
	 *            should be relative to the root of the file's class path.
	 * @param library
	 *            The path to the swfmill library XML file that will be used to
	 *            compile assets.
	 * @param classPaths
	 *            A list of directory paths (absolute, or relative to working
	 *            directory) that will be searched for ActionScript classes
	 */
	public BuildInfo(String fileToCompile, String library,
			List<String> classPaths) {
		this.setFileToCompile(fileToCompile);
		this.setLibraryToCompile(library);
		this.setClassPaths(classPaths);
	}

	/**
	 * @return The path to the file that will be compiled into the swf, along
	 *         with all its dependencies.
	 */
	public String getFileToCompile() {
		return fileToCompile;
	}

	/**
	 * @param fileToCompile
	 *            The path to the class file that is compiled first. All
	 *            dependencies will also be compiled. Please note that this path
	 *            should be relative to the root of the file's class path.
	 */
	public void setFileToCompile(String fileToCompile) {
		this.fileToCompile = fileToCompile;
	}

	/**
	 * The path to the swfmill library XML file used to build an assets SWF. If
	 * swfmill is not being used for this build, <code>null</code> is
	 * returned.
	 * 
	 * @return The swfmill library XML file, or <code>null</code>
	 */
	public String getLibraryToCompile() {
		return library;
	}

	/**
	 * Sets the path to the swfmill library XML file used to build an assets
	 * SWF. If swfmill is not being used for this build, the value should be
	 * <code>null</code>.
	 * 
	 * @param pathToLibrary
	 *            The path to the swfmill library XML file
	 */
	public void setLibraryToCompile(String pathToLibrary) {
		this.library = pathToLibrary;
	}

	/**
	 * Whether this build uses swfmill to build an assets SWF before using it as
	 * input to MTASC.
	 * 
	 * @return <code>true</code> if swfmill is used
	 */
	public boolean buildUsesSwfmill() {
		return this.library != null;
	}

	/**
	 * @return A list of paths (absolute or relative to working directory) that
	 *         will be searched for class files.
	 */
	public List<String> getClassPaths() {
		return classPaths;
	}

	/**
	 * @param classPaths
	 *            A list of paths (absolute or relative to working directory)
	 *            that will be searched for class files.
	 */
	public void setClassPaths(List<String> classPaths) {
		this.classPaths = classPaths;
	}

	/**
	 * <p>
	 * Sets a list of packages that will be packed into the build.
	 * </p>
	 * <p>
	 * If the package name ends in <code>.*</code>, then the packing will be
	 * recursive.
	 * </p>
	 * 
	 * @param packedPackages
	 *            the packages that will be packed into the build
	 */
	public void setPackedPackages(List<String> packedPackages) {
		this.packedPackages = packedPackages;
	}

	/**
	 * Returns a list of packages that will be packed into the build.
	 * 
	 * @return the names of packages that will be packed into the build.
	 */
	public List<String> getPackedPackages() {
		return packedPackages;
	}

	/**
	 * Sets a list of classes and packages that will be excluded from the build.
	 * 
	 * @param exclusions
	 *            the packages and classes to exclude from the build
	 */
	public void setExclusions(List<String> exclusions) {
		this.exclusions = exclusions;
	}

	/**
	 * Returns a list of classes and packages that will be excluded from the
	 * build.
	 * 
	 * @return the packages and classes that will be excluded from the build
	 */
	public List<String> getExclusions() {
		return exclusions;
	}

	/**
	 * Sets whether all classes except for those specified by
	 * {@link #getPackedPackages()} will be excluded from the build.
	 * 
	 * @param excludeAllButPacked
	 *            <code>true</code> to exclude all but the packed packages, or
	 *            <code>false</code> otherwise
	 */
	public void setExcludeAllButPacked(boolean excludeAllButPacked) {
		this.excludeAllButPacked = excludeAllButPacked;
	}

	/**
	 * Sets whether all classes except for those specified by
	 * {@link #getPackedPackages()} will be excluded from the build.
	 * 
	 * @return <code>true</code> if all but the packed packages are excluded,
	 *         or <code>false</code> otherwise
	 */
	public boolean excludeAllButPacked() {
		return excludeAllButPacked;
	}

	/**
	 * Returns the resulting SWF's width.
	 * 
	 * @return The width of the resulting swf
	 */
	public int getWidth() {
		return width;
	}

	/**
	 * Sets the resulting SWF's width.
	 * 
	 * @param width
	 *            The width of the resulting swf
	 */
	public void setWidth(int width) {
		this.width = width;
	}

	/**
	 * Returns the resulting SWF's height.
	 * 
	 * @return The height of the resulting swf
	 */
	public int getHeight() {
		return height;
	}

	/**
	 * Sets the resulting SWF's height.
	 * 
	 * @param height
	 *            The height of the resulting swf
	 */
	public void setHeight(int height) {
		this.height = height;
	}

	/**
	 * Returns the resulting SWF's frame rate.
	 * 
	 * @return The frame rate of the resulting swf
	 */
	public int getFPS() {
		return fps;
	}

	/**
	 * Sets the resulting SWF's frame rate.
	 * 
	 * @param fps
	 *            The frame rate of the resulting swf
	 */
	public void setFPS(int fps) {
		this.fps = fps;
	}

	/**
	 * Returns whether the build has a main method.
	 * 
	 * @return <code>true</code> if the resulting swf has a main method. The
	 *         default is <code>true</code>.
	 */
	public boolean hasMainMethod() {
		return main;
	}

	/**
	 * Sets whether the build has a main method.
	 * 
	 * @param main
	 *            <code>true</code> if the resulting swf has a main method.
	 */
	public void setHasMainMethod(boolean main) {
		this.main = main;
	}

	/**
	 * Returns whether the build will be done in strict mode.
	 * 
	 * @return <code>true</code> if the classes should be compiled in strict
	 *         mode. The default is <code>false</code>.
	 */
	public boolean isStrict() {
		return strict;
	}

	/**
	 * Sets whether the build will be done in strict mode.
	 * 
	 * @param flag
	 *            <code>true</code> if the classes should be compiled in
	 *            strict mode.
	 */
	public void setStrict(boolean flag) {
		this.strict = flag;
	}

	/**
	 * @return the function used in place of <code>trace</code> in the code
	 */
	public String getTraceFunction() {
		return traceFunction;
	}

	/**
	 * @param traceFunction
	 *            the function to use instead of <code>trace</code>. A value
	 *            of <code>"no"</code> means that trace functions will be
	 *            removed from code during compilation.
	 */
	public void setTraceFunction(String traceFunction) {
		this.traceFunction = traceFunction;
	}

	/**
	 * @return The version of the resulting swf. The default is 8.
	 */
	public int getVersion() {
		return version;
	}

	/**
	 * @param version
	 *            The version of the resulting swf. Accepted values are 6, 7 and
	 *            8.
	 */
	public void setVersion(int version) {
		this.version = version;
	}

	//
	// ============= HELPER METHODS
	//

	/**
	 * Builds a single element list containing <code>str</code>.
	 */
	private static List<String> listWithString(String str) {
		List<String> list = new ArrayList<String>();
		list.add(str);
		return list;
	}
}
